/**
 * User: alesj
 * Date: 2004.8.25
 * Time: 12:44:03
 */

package com.generalynx.common.web.tags;

import com.generalynx.common.constants.WebConst;
import com.generalynx.common.data.IEntity;
import com.generalynx.common.resources.Resources;
import com.generalynx.common.utils.Tools;
import com.generalynx.ecos.utils.BeanConst;
import org.hibernate.type.AbstractComponentType;
import org.hibernate.type.Type;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.util.Collection;
import java.util.Iterator;

public class ADEBeanTag extends TagSupport {

    private static final String TYPE = "text";
    private static final String CHECK = "checkbox";
    private static final String CHECKED = "checked=\"checked\"";
    private static String c_input;

    private boolean editable = true;
    private String entityName = WebConst.DATA;

    private ADEContextFactory contextFactory;

    public int doStartTag() throws JspException {
        try {
            c_input = "<tr><td><b>{0}</b></td><td><input name=\"{1}\" type=\"{2}\"" +
                    (editable ? "" : " disabled=\"disabled\"") + " value=\"{3}\" {4}/></td></tr>\n";

            HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
            BeanFactory beanFactory = (BeanFactory) request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            contextFactory = (ADEContextFactory)beanFactory.getBean(BeanConst.ADE_CONTEXT_FACTORY);

            IEntity entity = (IEntity) request.getAttribute(entityName);

            MetaDataHolder metaData = contextFactory.getMetaData(entity);
            String[] labels = metaData.getLabels();
            String[] props = metaData.getProps();
            Type[] types = metaData.getTypes();
            Object[] values = metaData.getValues();
            boolean[] includes = metaData.getIncludes();
            SelectRenderer[] renderers = metaData.getRenderers();

            StringBuffer buffer = new StringBuffer("<table class=\"ade\">\n");
            buffer.append(makeForm(entity, labels, props, types, values, includes, renderers, null));
            buffer.append("</table>");

            pageContext.getOut().write(buffer.toString());
            return TagSupport.EVAL_PAGE;
        } catch (Exception e) {
            throw new JspException(e);
        }
    }

    private StringBuffer makeForm(IEntity entity,
                                  String[] labels,
                                  String[] props,
                                  Type[] types,
                                  Object[] values,
                                  boolean[] includes,
                                  SelectRenderer[] renderers,
                                  String prefix)
            throws Exception {

        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < props.length; i++) {
            if (includes[i]) {
                if (types[i].isCollectionType()) {
                    //do nothing
                } else if (types[i].isComponentType()) {
                    buffer.append(renderComponent(entity,
                                                  props[i],
                                                  (AbstractComponentType)types[i],
                                                  values[i],
                                                  prefix));
                } else if (types[i].isAssociationType()) {
                    buffer.append(renderAssociation(labels[i], props[i], values[i], renderers[i], prefix));
                } else {
                    buffer.append(renderObject(labels[i], props[i], values[i], renderers[i], prefix));
                }
            }
        }
        return buffer;
    }

    private String renderObject(String label, String prop, Object value, SelectRenderer selectRenderer, String prefix)
            throws Exception {
        String name = (prefix != null ? prefix + "." + prop : prop);
        if (value == null) {
            value = "";
        }
        if (label == null) {
            try {
                label = Resources.getString(prop.toUpperCase());
            } catch (Exception e) {
                label = prop;
            }
        }
        String type = TYPE;
        String checked = "";
        if (value instanceof Boolean) {
            type = CHECK;
            Boolean bVal = (Boolean)value;
            if (bVal.booleanValue()) {
                checked = CHECKED;
            }
            value = Boolean.TRUE;
        } else if (selectRenderer != null) {
            Collection selectCollection = selectRenderer.getSelectCollection();
            if (selectCollection != null) {
                return doSelect(selectCollection, label, name, value);
            }
        }
        Object[] args = {label, name, type, value, checked};
        String result;
        synchronized(c_input) {
            result = Tools.formatWithToStringTransform(c_input, args);
        }
        return result;
    }

    private String renderAssociation(String label, String prop, Object value, SelectRenderer renderer, String prefix)
            throws Exception {
        if (value != null) {
            String newProp = prop + ".id";
            IEntity entity = (IEntity)value;
            return renderObject(label, newProp, new Integer(entity.getId()), renderer, prefix);
        } else {
            return "";
        }
    }

    private StringBuffer renderComponent(IEntity entity,
                                         String prop,
                                         AbstractComponentType act,
                                         Object value,
                                         String prefix) throws Exception {
        if (value == null) {
            value = act.getReturnedClass().newInstance();
        }

        MetaDataHolder metaData = contextFactory.getComponentMetaData(entity, prop, act, value, prefix);
        String[] labels = metaData.getLabels();
        String[] props = metaData.getProps();
        Type[] types = metaData.getTypes();
        Object[] values = metaData.getValues();
        boolean[] includes = metaData.getIncludes();
        SelectRenderer[] renderers = metaData.getRenderers();

        String newPrefix = (prefix != null ? prefix + "." + prop : prop);
        return makeForm(entity, labels, props, types, values, includes, renderers, newPrefix);
    }

    private String doSelect(Collection selectCollection, String label, String name, Object value) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<tr><td><b>");
        buffer.append(label);
        buffer.append("</b></td><td>");
        buffer.append("<select name=\"");
        buffer.append(name);
        buffer.append("\" size=\"1\">\n");
        Iterator iterator = selectCollection.iterator();
        while (iterator.hasNext()) {
            SelectOption selectOption = (SelectOption)iterator.next();
            buffer.append("<option value=\"");
            buffer.append(selectOption.getIdentifier());
            boolean equals = selectOption.isAssociation() ?
                    selectOption.getIdentifier().equals(value) :
                    selectOption.equals(value);
            if (equals) {
                buffer.append("\" selected=\"selected");
            }
            buffer.append("\">");
            buffer.append(selectOption.getLabel());
            buffer.append("</option>\n");
        }
        buffer.append("</select></td></tr>\n");
        return buffer.toString();
    }

    public void setEditable(boolean editable) {
        this.editable = editable;
    }

    public void setEntityName(String entityName) {
        this.entityName = entityName;
    }

    /**
     * So that all boolean values are set to false in order for binder to set them on true
     * if checkbox selected - see ADEController.
     */
    /*
    private void postApplyBooleanValues(IEntity entity, ClassPersister cp) throws Exception {
        Object[] values = cp.getPropertyValues(entity);
        for(int i = 0; i < values.length; i++) {
            if (values[i] instanceof Boolean) {
                cp.setPropertyValue(entity, i, new Boolean(false));
            }
        }
    }
    */

}
